﻿using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace BookDemo.S07.S711
{
    class S711 : IBookDemo
    {
        public void Main(string[] args)
        {
            var random = new Random();
            var optionsMonitor = new ServiceCollection()
                .AddOptions()
                .Configure<FoobarOptions>(TimeSpan.FromSeconds(1))
                .Configure<FoobarOptions>(foobar =>
                {
                    foobar.Foo = random.Next(10, 100);
                    foobar.Bar = random.Next(10, 100);
                })
                .BuildServiceProvider()
                .GetRequiredService<IOptionsMonitor<FoobarOptions>>();

            optionsMonitor.OnChange(foobar => Console.WriteLine($"[{DateTime.Now}]{foobar}"));
            Console.Read();
        }
    }
    public class FoobarOptions
    {
        public int Foo { get; set; }
        public int Bar { get; set; }
        public override string ToString() => $"Foo:{Foo}, Bar:{Bar}";
    }
    public static class ServiceCollectionExtensions
    {
        public static IServiceCollection Configure<TOptions>(this IServiceCollection services, string name, TimeSpan refreshInterval)
           => services.AddSingleton<IOptionsChangeTokenSource<TOptions>>(new TimedRefreshTokenSource<TOptions>(refreshInterval, name));
        public static IServiceCollection Configure<TOptions>(this IServiceCollection services, TimeSpan refreshInterval)
           => services.Configure<TOptions>(Options.DefaultName, refreshInterval);
    }
    public class TimedRefreshTokenSource<TOptions> : IOptionsChangeTokenSource<TOptions>
    {
        private OptionsChangeToken _changeToken;
        public string Name { get; }

        public TimedRefreshTokenSource(TimeSpan interval, string name)
        {
            Name = name ?? Options.DefaultName;
            _changeToken = new OptionsChangeToken();
            ChangeToken.OnChange(() => new CancellationChangeToken(new CancellationTokenSource(interval).Token),
                () =>
                {
                    var previous = Interlocked.Exchange(ref _changeToken, new OptionsChangeToken());
                    previous.OnChange();
                });
        }

        public IChangeToken GetChangeToken() => _changeToken;

        private class OptionsChangeToken : IChangeToken
        {
            private readonly CancellationTokenSource _tokenSource;

            public OptionsChangeToken() => _tokenSource = new CancellationTokenSource();
            public bool HasChanged => _tokenSource.Token.IsCancellationRequested;
            public bool ActiveChangeCallbacks => true;
            public IDisposable RegisterChangeCallback(Action<object> callback, object state) => _tokenSource.Token.Register(callback, state);
            public void OnChange() => _tokenSource.Cancel();
        }
    }
}
